#ifndef _CANARY2_HTTP_PARSER_CALLBACKS_H_
#define _CANARY2_HTTP_PARSER_CALLBACKS_H_

#include <llhttp.h>
#include <utility>
#include <type_traits>
#include "concepts.h"
#include "../util/assert.h"

namespace wilson::http
{
    // ::llhttp_data_cb

    template<typename Parser> 
    int on_url(::llhttp_t* p, const char* at, size_t length)
    {
        auto& builder = reinterpret_cast<Parser*>(p->data)->builder();
        using string_type = ::std::remove_reference_t<decltype(builder)>::string_type;
        builder.set_url(string_type{ at, length });
        return HPE_OK;
    }

    template<typename Parser> 
    requires (is_response<typename Parser::generate_type>)
    int on_status(::llhttp_t* p, const char* at, size_t length)
    {
        auto& builder = reinterpret_cast<Parser*>(p->data)->builder();
        using string_type = ::std::remove_reference_t<decltype(builder)>::string_type;
        string_type status_str{ at, length };
        builder.set_status(to_status(status_str));
        return HPE_OK;
    }

    template<typename Parser>
    requires (is_request<typename Parser::generate_type>)
    int on_status([[maybe_unused]] ::llhttp_t*, 
                  [[maybe_unused]] const char*, 
                  [[maybe_unused]] size_t)
    {
        return HPE_OK;
    }

    template<typename Parser> 
    int on_header_field(::llhttp_t* p, const char* at, size_t length)
    {
        using string_type = 
            ::std::remove_reference_t<
                decltype(::std::declval<Parser>().builder())
            >::string_type;

        reinterpret_cast<Parser*>(p->data)
            ->m_header_field_trampoline = string_type{ at, length };
        return HPE_OK;
    }

    template<typename Parser> 
    int on_header_value(::llhttp_t* p, const char* at, size_t length)
    {
        auto& parser = *reinterpret_cast<Parser*>(p->data);
        auto& builder = parser.builder();
        using string_type = ::std::remove_reference_t<decltype(builder)>::string_type;
        string_type header_field = parser.m_header_field_trampoline;
        WILSON_ASSERT(!header_field.empty());
        builder.set_header(::std::move(header_field), string_type{ at, length });
        return HPE_OK;
    }

    template<typename Parser> 
    int on_body(::llhttp_t* p, const char* at, size_t length)
    {
        auto& builder = reinterpret_cast<Parser*>(p->data)->builder();
        using string_type = ::std::remove_reference_t<decltype(builder)>::string_type;
        builder.set_body(string_type{ at, length });
        return HPE_OK;
    }


    // ::llhttp_cb

    template<typename Parser> 
    int on_message_begin(::llhttp_t* p)
    {
        auto& builder = reinterpret_cast<Parser*>(p->data)->builder();
        using string_type = ::std::remove_reference_t<decltype(builder)>::string_type;
        return HPE_OK;
    }

    template<typename Parser> 
    int on_headers_complete([[maybe_unused]] ::llhttp_t*)
    {
        /* After the header parsed completely,
         * information about this request 
         * such as `method` `version` etc.
         * are ready, interrupt the parsing execution and 
         * return to the position where `llhttp_execute` called,
         * parser will fill those information into message object
         * by request/response builder's `set_*` methods.
         *
         * After those settings done, parser will call
         * `llhttp_resume` back to the llhttp parsing execution.
         */
        return HPE_PAUSED;
    }

    template<typename Parser> 
    int on_message_complete(::llhttp_t* p)
    {
        return HPE_OK;
    }

    template<typename Parser> 
    int on_chunk_header(::llhttp_t* p)
    {
        return HPE_OK;
    }

    template<typename Parser> 
    int on_chunk_complete(::llhttp_t* p)
    {
        return HPE_OK;
    }

    template<typename Parser> 
    int on_url_complete(::llhttp_t* p)
    {
        return HPE_OK;
    }

    template<typename Parser> 
    int on_status_complete(::llhttp_t* p)
    {
        return HPE_OK;
    }

    template<typename Parser> 
    int on_header_field_complete(::llhttp_t* p)
    {
        return HPE_OK;
    }

    template<typename Parser> 
    int on_header_value_complete(::llhttp_t* p)
    {
        return HPE_OK;
    }
}

#endif
